import math
import random
import time
import sys
import Gnuplot

from numpy.core.numeric import zeros

# Constantes
N = 10
VENTAJA = 2
SALIDA = 9
ENTRADA = 0

#Clase.
class Proceso:
	def __init__(self,matriz,epsilon,nombre):
		"""Constructor de Clase"""
		self.matriz = matriz
		self.v = self.inic_v()
		self.ve = []
		self.vacum = self.inic_vacum()
		self.macum = self.inic_macum()
		self.paso = 0
		self.hits = []
		self.esq = -1
		self.esq_ant = -1
		self.vueltas = []
		self.victorias = 0
		self.epsilon = epsilon
		self.historia_ve = [[],[],[],[],[],[],[],[],[],[]]
		self.historia_media = []
		self.historia_victoria = []
		self.nombre = nombre

	def inic_v(self):
		v = []
		for i in range(N):
			v.append( 1 / float(N) )
		return v

	def inic_vacum(self):
		vacum = []
		vacum.append(self.v[0])
		for i in range(N):
			vacum.append(vacum[i] + self.v[i])
		return vacum
	
	def inic_macum(self):
		macum = zeros((N,N))
		for j in range(N):
			macum[0][j] = self.matriz[0][j];
			for i in range(1,N):
				macum[i][j] = (self.matriz[i][j] + macum[i-1][j]);
		return macum
			
	def primer_paso(self):
		self.esq = ENTRADA
		self.paso = 1
		return
		
	# Dado un paso, retorna el proximo.
	def sig_dado_ant(self):
		self.esq_ant = self.esq
		r = random.random()
		i = 0
		self.paso += 1

		while (r > self.macum[i][self.esq] and i+1<N):
			i += 1
		self.esq = i

		return
			
	def guardar_historia_ve(self,v,archivo):
		for i in range(N):
			self.historia_ve[i].append(v[i])
			archivo.write(str(v)+"\n")
		return
	
	# Calcula el vector estacionario ve
	def calcular_ve(self):
		print "Calculando V* para",self.nombre,"..."
		self.primer_paso() # estado/esquina inicial
		self.hits = zeros(N) # cant de veces (hits) que pase por cada esquina
		v = zeros(N) # vector a retornar
		v_ant = zeros(N)
		min_cant_pasos = 100
		archivo = open(self.nombre+"-ve","w")
		
		while ((self.paso < min_cant_pasos) or (not self.converge_v(v,v_ant))):

			v_ant[self.esq] = v[self.esq]
			v[self.esq] = self.hits[self.esq] / float(self.paso+1)
			self.hits[self.esq] += 1
			self.sig_dado_ant()
			self.guardar_historia_ve(v,archivo)
		self.ve = v
		archivo.close()

		return v
		
	# Todos los valores del vector actual converge?
	def converge_v(self, actual, anterior):
		for i in range(N):
			if (abs(actual[i]-anterior[i]) > self.epsilon):
				return False
		return True
		
	def converge(self,actual,anterior):
		if ( abs(actual-anterior) > self.epsilon ):
			return False
		else:
			return True
		
	def media_tvuelta(self):
	
		self.vueltas = []	
		min_vueltas = 100

		media = 0.0
		media_ant = -1
		while ( not self.converge(media,media_ant) ):

			self.primer_paso()
			while ( self.esq != SALIDA ):
				self.sig_dado_ant()

			self.vueltas.append(self.paso)
			media_ant = media
			media = sum(self.vueltas) / float(len(self.vueltas))
			self.guardar_media(media)
		
		#print "en", len(self.vueltas), "vueltas"
		return media
		
	def guardar_media(self,media):
		ar = open(self.nombre+"-media","a")
		self.historia_media.append(media)
		ar.write(str(media))
		ar.write("\n")
		ar.close()	

class Escape:
	def __init__(self):
		self.muestras = 10
		self.vdrno = zeros( (self.muestras,2) )
		self.vbond = zeros( (self.muestras,2) )

	def guardar_datos(self,p1,p2,i):
		self.vdrno[i][0] = p1.paso
		self.vdrno[i][1] = p1.esq

		self.vbond[i][0] = p2.paso
		self.vbond[i][1] = p2.esq
		return
		
	def gano_p1(self,p1,p2):
		return ( p1.esq == ENTRADA and p1.esq_ant == SALIDA )
		
	def gano_p2(self,p1,p2):
		return ( p1.esq == p2.esq )
	
	def mover(self,p1,p2):
		""" Simula un juego hasta que uno gane """
		p1.primer_paso()
		for p in range(VENTAJA):
			p1.sig_dado_ant()

		p2.primer_paso()
		# hago pasos mientras que el proceso 1 no gane y el proceso 2 no gane
		# el proceso 1 gana si va del 9 al 0
		# el proceso 2 gana si el proceso 2 se encuentra en el mismo casillero que el proceso 1
		while ( not self.gano_p1(p1,p2) and not self.gano_p2(p1,p2) ):
			p1.sig_dado_ant()
			p2.sig_dado_ant()

		""" quien gano? """
		if ( self.gano_p1(p1,p2) ): #gano p1
			p1.victorias += 1
			return 1
		elif ( self.gano_p2(p1,p2) ): # gano p2
			p2.victorias += 1
			return 2
		
	def simular(self, p1, p2):
		
		""" Probabilidad de ganar de p1 (drno escapa) """
		print "\nSimulado persecusion y escape...."
		p_ganar1 = -1
		p_ganar2 = -1
		p_ganar1_ant = -3
		ganador = 0
		i = 0
		min_cant = 100
		
		while ( i < min_cant or not(p1.converge(p_ganar1, p_ganar1_ant)) ):

			self.mover(p1,p2)
			#self.guardar_datos(p1,p2,i)			
			i += 1
			
			p_ganar1_ant = p_ganar1
			p_ganar1 = p1.victorias / float(i)
			p_ganar2 = p2.victorias / float(i)
			
			self.guardar_victorias(p1,p2,p_ganar1,p_ganar2)
			
		self.guardar_archivo(p1,p1.historia_victoria,"-vict")
		self.guardar_archivo(p2,p2.historia_victoria,"-vict")

		print "P(gana drno) ",p_ganar1
		print "P(gana james) ",p_ganar2
		print "suma:",p_ganar1 + p_ganar2

		return i
		
	def guardar_archivo(self,p,datos,etiqueta):
		""" guarda arreglo datos  en archivo """
		path = p.nombre+etiqueta
		archivo = open(path,"a")
		for i in range(len(datos)):
			archivo.write( str(datos[i]) )			
			archivo.write("\n")
		archivo.close()	
	
	
	def guardar_victorias(self,p1,p2,p_ganar1,p_ganar2):
		p1.historia_victoria.append(p_ganar1)
		p2.historia_victoria.append(p_ganar2)
		return	
		
		

	def correlacion_cruzada(self,p1,p2):
	
		print "\nCorrelacion Cruzada en estado Estacionario: "
		correlacion = 0.0
		
		if ( len(p1.ve) == 0 and len(p2.ve) == 0 ):	#si NO fueron calculados
			p1.calcular_ve()
			p2.calcular_ve()
		
		for i in range(N):
			for j in range(N):
				correlacion += float(i*j*p1.ve[i]*p2.ve[j])

		return correlacion
		
			
# Matrices (se podrian cargar desde archivos)
drno_mat = [
	[0,0.15,0,0,0,0,0,0,0,0.7],
	[1.0,0,0.3,0,0.3,0,0,0,0,0],
	[0,0.15,0,1.0,0,0,0,0,0,0],
	[0,0,0.7,0,0,0,0,0,0,0],
	[0,0.7,0,0,0,0,0.15,0,0,0],
	[0,0,0,0,0,0,0.15,0,0,0],
	[0,0,0,0,0.7,1.0,0,0.15,0,0],
	[0,0,0,0,0,0,0.7,0,1.0,0.3],
	[0,0,0,0,0,0,0,0.15,0,0],
	[0,0,0,0,0,0,0,0.7,0,0],
]

bond_mat = [
	[0,0.33,0,0,0,0,0,0,0,0.5],
	[1,0,0.5,0,0.5,0,0,0,0,0],
	[0,0.33,0,1,0,0,0,0,0,0],
	[0,0,0.5,0,0,0,0,0,0,0],
	[0,0.33,0,0,0,0,0.33,0,0,0],
	[0,0,0,0,0,0,0.33,0,0,0],
	[0,0,0,0,0.5,1,0,0.33,0,0],
	[0,0,0,0,0,0,0.33,0,1,0.5],
	[0,0,0,0,0,0,0,0.33,0,0],
	[0,0,0,0,0,0,0,0.33,0,0],
]



if (len(sys.argv) == 1):
	print "*************************************************************************************"
	print "Usted puede agregar un valor a el epsilon como parametro sino por defecto sera 0.0001"
	print "*************************************************************************************"
	drno = Proceso(drno_mat,0.0001,"drno")
	bond = Proceso(bond_mat,0.0001,"bond")
elif(len(sys.argv) > 1):
	print sys.argv[1]
	drno = Proceso(drno_mat,float(sys.argv[1]),"drno")
	bond = Proceso(bond_mat,float(sys.argv[1]),"bond")
	

print "\nVectores  Estacionarios V*"
print drno.calcular_ve(),"en",drno.paso ,"pasos"
print bond.calcular_ve(),"en",bond.paso,"pasos"

skape = Escape()
skape.simular(drno,bond)

print "\nTiempo medio en ir desde la Entrada a la Salida:"
print "DrNo:",drno.media_tvuelta(),"seg"
print "JBond:",bond.media_tvuelta(),"seg"

#print skape.correlacion_cruzada(drno,bond)

def graficar_ve(p):
	gp = Gnuplot.Gnuplot(persist = 1)
	gp('set data style lines')
	plots = []
	for i in range(N):
		titulo = "Esquina "+p.nombre
		titulo += str(i)
		plot = Gnuplot.PlotItems.Data(p.historia_ve[i], title=titulo )
		plots.append(plot)
	gp.plot(*plots)
	return

def graficar_data(data1,data2,titulo):

	""" Grafica con gnuplot los datos de ambas listas """ 
	gp = Gnuplot.Gnuplot(persist = 1)
	gp('set data style lines')
	plot1 = Gnuplot.PlotItems.Data(data1, title=titulo+"DrNo")
	plot2 = Gnuplot.PlotItems.Data(data2, title=titulo+"JBond")
	gp.plot(plot1,plot2)
	return
	
graficar_ve(drno)
graficar_ve(bond)
graficar_data(drno.historia_media,bond.historia_media, "T medio de SALIDA")
graficar_data(drno.historia_victoria,bond.historia_victoria,"#Victorias")
